﻿namespace Unity.SerializationLogic
{
    using Mono.Cecil;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using Unity.CecilTools;
    using Unity.CecilTools.Extensions;

    public static class UnitySerializationLogic
    {
        [CompilerGenerated]
        private static Func<CustomAttribute, TypeReference> f_amcache;/*<>f__am$cache0*/

        private static bool CanFieldContainUnityEngineObjectReference(TypeReference typeReference, FieldDefinition t)
        {
            if (t.FieldType == typeReference)
            {
                return false;
            }
            if (!WillUnitySerialize(t))
            {
                return false;
            }
            return true;
        }

        private static bool CanTypeContainUnityEngineObjectReference(TypeReference typeReference)
        {
            if (IsUnityEngineObject(typeReference))
            {
                return true;
            }
            if (typeReference.IsEnum())
            {
                return false;
            }
            if (IsSerializablePrimitive(typeReference))
            {
                return false;
            }
            if (IsSupportedCollection(typeReference))
            {
                return CanTypeContainUnityEngineObjectReference(CecilUtils.ElementTypeOfCollection(typeReference));
            }
            TypeDefinition definition = typeReference.Resolve();
            if (definition == null)
            {
                return false;
            }
            return HasFieldsThatCanContainUnityEngineObjectReferences(definition);
        }

        private static IEnumerable<TypeReference> FieldAttributes(FieldDefinition field)
        {
            if (f_amcache == null)
            {
                f_amcache = _ => _.AttributeType;
            }
            return Enumerable.Select<CustomAttribute, TypeReference>(field.CustomAttributes, f_amcache);
        }

        private static bool HasFieldsThatCanContainUnityEngineObjectReferences(TypeDefinition definition)
        {
            HasFieldsThatCanContainUnityEngineObjectReferencesAnonStorey storey = new HasFieldsThatCanContainUnityEngineObjectReferencesAnonStorey {
                definition = definition
            };
            return Enumerable.Any<FieldDefinition>(storey.definition.Fields, new Func<FieldDefinition, bool>(storey.m_0));
        }

        public static bool HasSerializeFieldAttribute(FieldDefinition field)
        {
            return Enumerable.Any<TypeReference>(FieldAttributes(field), new Func<TypeReference, bool>(UnityEngineTypePredicates.IsSerializeFieldAttribute));
        }

        private static bool IsConst(FieldDefinition fieldDefinition)
        {
            return (fieldDefinition.IsLiteral && !fieldDefinition.IsInitOnly);
        }

        private static bool IsFieldTypeSerializable(TypeReference fieldType)
        {
            return (IsTypeSerializable(fieldType) || IsSupportedCollection(fieldType));
        }

        private static bool IsSerializablePrimitive(TypeReference fieldType)
        {
            MetadataType metadataType = fieldType.MetadataType;
            switch (metadataType)
            {
                case MetadataType.Int32:
                case MetadataType.Single:
                case MetadataType.Double:
                case MetadataType.String:
                    break;

                default:
                    switch (metadataType)
                    {
                        case MetadataType.Boolean:
                        case MetadataType.Byte:
                            break;

                        case MetadataType.Char:
                        case MetadataType.SByte:
                            goto Label_004A;

                        default:
                            goto Label_004A;
                    }
                    break;
            }
            return true;
        Label_004A:
            return false;
        }

        public static bool IsSupportedCollection(TypeReference fieldType)
        {
            if (!(fieldType is ArrayType) && !CecilUtils.IsGenericList(fieldType))
            {
                return false;
            }
            return IsTypeSerializable(CecilUtils.ElementTypeOfCollection(fieldType));
        }

        private static bool IsTypeSerializable(TypeReference fieldType)
        {
            return ((((IsSerializablePrimitive(fieldType) || fieldType.IsEnum()) || (IsUnityEngineObject(fieldType) || UnityEngineTypePredicates.IsAnimationCurve(fieldType))) || UnityEngineTypePredicates.IsGradient(fieldType)) || ShouldImplementIDeserializable(fieldType));
        }

        private static bool IsUnityEngineObject(TypeReference fieldType)
        {
            return UnityEngineTypePredicates.IsUnityEngineObject(fieldType);
        }

        public static bool ShouldFieldBePPtrRemapped(FieldDefinition fieldDefinition)
        {
            if (!WillUnitySerialize(fieldDefinition))
            {
                return false;
            }
            return CanTypeContainUnityEngineObjectReference(fieldDefinition.FieldType);
        }

        private static bool ShouldHaveHadAllFieldsPublic(FieldDefinition field)
        {
            return UnityEngineTypePredicates.IsUnityEngineValueType(field.DeclaringType);
        }

        public static bool ShouldImplementIDeserializable(TypeReference typeDeclaration)
        {
            if (typeDeclaration.IsEnum())
            {
                return false;
            }
            if (typeDeclaration.MetadataType == MetadataType.Object)
            {
                return false;
            }
            if (typeDeclaration.FullName.StartsWith("System."))
            {
                return false;
            }
            if (typeDeclaration.IsStruct() && !typeDeclaration.FullName.StartsWith("UnityEngine."))
            {
                return false;
            }
            if (typeDeclaration.IsArray)
            {
                return false;
            }
            return (((UnityEngineTypePredicates.IsMonoBehaviour(typeDeclaration) || UnityEngineTypePredicates.IsScriptableObject(typeDeclaration)) || typeDeclaration.CheckedResolve().IsSerializable) || UnityEngineTypePredicates.ShouldHaveHadSerializableAttribute(typeDeclaration));
        }

        private static TypeReference TypeOf(FieldDefinition fieldDefinition)
        {
            return fieldDefinition.FieldType;
        }

        public static bool WillUnitySerialize(FieldDefinition fieldDefinition)
        {
            if (fieldDefinition == null)
            {
                return false;
            }
            if ((fieldDefinition.IsStatic || IsConst(fieldDefinition)) || (fieldDefinition.IsNotSerialized || fieldDefinition.IsInitOnly))
            {
                return false;
            }
            if (!IsFieldTypeSerializable(TypeOf(fieldDefinition)))
            {
                return false;
            }
            if ((!fieldDefinition.IsPublic && !HasSerializeFieldAttribute(fieldDefinition)) && !ShouldHaveHadAllFieldsPublic(fieldDefinition))
            {
                return false;
            }
            if (fieldDefinition.FullName == "UnityScript.Lang.Array")
            {
                return false;
            }
            return true;
        }

        [CompilerGenerated]
        /*<HasFieldsThatCanContainUnityEngineObjectReferences>c__AnonStorey0*/
        private sealed class HasFieldsThatCanContainUnityEngineObjectReferencesAnonStorey
        {
            internal TypeDefinition definition;

            /*<>m__0*/
            internal bool m_0(FieldDefinition t)
            {
                return UnitySerializationLogic.CanFieldContainUnityEngineObjectReference(this.definition, t);
            }
        }
    }
}

